import Web3 from 'web3';
import {ethNodeUrl} from '../../../global';
import { toWei, toHex, fromWei } from 'web3-utils';
import EthConfig from '../../../database/eth/EthConfig';
import des from 'decimal.js';
import { logger } from '../../../models/LogModel';
import Tx from 'ethereumjs-tx';
import { TransactionMessage } from '../../../Interface/TransactionMessage';
import { post } from 'request';
import EthCoinType from '../../../database/eth/EthCoinType';
import EthTransfer from '../../../database/eth/EthTransfer';

class EthExtract {

  static web3 = new Web3(ethNodeUrl)
  private transferToAddress: string;
  private transferToAmount: string;
  private orderId: string;

  constructor(to: string, amount: number, orderId: string| number) {
    this.transferToAddress = to;
    this.transferToAmount = toWei(amount.toString());
  }

  async send() {
    const extractAddress = await EthConfig.getParam('extract_address');
    const ethAmount = await EthExtract.web3.eth.getBalance(extractAddress);
    
    const gasPrice = await EthExtract.web3.eth.getGasPrice();
    const gasLimit = await EthExtract.web3.eth.estimateGas({
      from: extractAddress,
      to: this.transferToAddress,
      value: this.transferToAmount
    });

    const gas = new des(gasPrice).mul(gasLimit);

    if(new des(ethAmount).sub(gas).sub(this.transferToAmount).lt(0)){
      logger('ethExtract').trace(`eth手续费不足或eth余额不足`);
      const transaction: TransactionMessage = {
        blockNum: null,
        hash: null,
        from: null,
        to: null,
        contract: null,
        amount: null,
        orderId: this.orderId
      }
      this.sendTransactionMessage(transaction);
      await Promise.reject('eth手续费不足或eth余额不足');
    }

    const number = await EthExtract.web3.eth.getTransactionCount(extractAddress);

    const rawTx = {
      nonce: toHex(number),
      gasPrice: toHex(gasPrice),
      gasLimit: toHex(gasLimit),
      to: this.transferToAddress,
      value: toHex(this.transferToAmount)
    }

    const tx = new Tx(rawTx);
    const extractAddressPrvateKey = await EthConfig.getParam('extract_address_private_key');
    tx.sign(Buffer.from(this.ethAddressPrivateKeyHandle(extractAddressPrvateKey), 'hex'));
    
    const serializedTx = tx.serialize();

    EthExtract.web3.eth.sendSignedTransaction('0x' + serializedTx.toString('hex'))
    .on('transactionHash', hash => {
      logger('ethExtract').trace(`eth提币 ${hash} amount ${fromWei(this.transferToAmount, 'ether')}`);
      const transaction: TransactionMessage = {
        blockNum: null,
        hash: hash,
        from: null,
        to: null,
        contract: null,
        amount: null,
        orderId: this.orderId
      }
      this.sendTransactionMessage(transaction);
      logger('ethExtract').trace(`eth提币 ${hash} amount ${fromWei(this.transferToAmount, 'ether')}`);
    })
    .on('receipt', async receipt => {
      const transaction: TransactionMessage = {
        blockNum: receipt.blockNumber,
        hash: receipt.transactionHash,
        from: receipt.from,
        to: receipt.to,
        contract: null,
        amount: fromWei(this.transferToAmount),
        orderId: this.orderId
      }
      this.sendTransactionMessage(transaction);
      const coinTypeId = await EthCoinType.getCoinTypeId('eth');
      EthTransfer.save(2, coinTypeId, transaction, '提币到用户地址');
      logger('ethExtract').trace(`eth提币 ${receipt.transactionHash} amount ${fromWei(this.transferToAmount, 'ether')} 提取成功`);
    }).catch(error => {
      const transaction: TransactionMessage = {
        blockNum: null,
        hash: null,
        from: null,
        to: null,
        contract: null,
        amount: null,
        orderId: this.orderId
      }
      this.sendTransactionMessage(transaction);
    })

  }

  /**
   * 处理私钥
   * @param {string} privateKey
   * @returns
   * @memberof EthExtract
   */
  ethAddressPrivateKeyHandle(privateKey: string) {
    if(privateKey.includes('0x')){
      return privateKey.split('0x')[1];
    }
    return privateKey;
  }

  /**
   * 发送交易通知到平台
   * @param {number} [num=1]
   * @memberof EthHandle
   */
  async sendTransactionMessage(transaction: TransactionMessage ,num: number = 1) {
    const sendUrl = await EthConfig.getParam('send_coin_callback_url');
    if(sendUrl) {
      post(sendUrl, {
        form: transaction
      }, (error, res, body) => {
        if(num < 5){
          if(error) {
            logger('ethHandle').debug(`eth 提币交易信息发送失败,检测到致命BUG。 ${num}`);
          }else{
            if(body === 'success') {
              logger('ethHandle').trace(`eth 提币交易信息发送成功---${transaction.hash} ${num}`);
            }else {
              logger('ethHandle').trace(`eth 提币交易信息发送失败---${transaction.hash} ${num}`);
              setTimeout(() => {
                this.sendTransactionMessage(transaction, num+=1)
              }, 5000)
            }
          }
        }
      });
    }
  }
}

export default EthExtract;